<?php
class SimpleLogger {
    /* 日志文件前缀 */
    private $file_prefix = '';

    /* 已经打开的日志文件指针 */
    private $fps = array();

    /* 是否自动输出到控制台/标准输出 */
    private $auto_output = true;

    /* 日志文件路径 */
    private $log_path = null;

    public function SimpleLogger($log_path, $opt_prefix = null) {
        if (!is_dir($log_path)) {
            if (@mkdir($log_path, 0777, true) === false) {
                printf("[%s][E] Make directory %s failed.\n",
                        date('H:i:s', time()), dirname($log_path));

                return ;
            }
        }

        $this->log_path = $log_path;
        if (!is_null($opt_prefix)) {
            $this->file_prefix .= $opt_prefix;
        }
    }


    /**
     * 清空日志内容
     *
     * @param {enum.<'info', 'error', 'warning'>} $opt_level 日志级别
     */
    public function truncate($opt_level = null) {
        $level_list = !is_null($opt_level) ? array($opt_level) : array('info', 'warning', 'error', 'trace');
        foreach ($level_list as $k=>$log_level) {
            if (!isset($this->fps[$log_level])) {
                $file_name = $this->log_path.'/'.(!empty($this->file_prefix) ? ($this->file_prefix.'-') : '').$log_level.'.log';
                if (($this->fps[$log_level] = fopen($file_name, 'wb')) === false) {
                    printf("[%s][E] Open log file %s failed.\n", date('H:i:s', time()), $file_name);
                }

                continue;
            }

            if (ftruncate($this->fps[$log_level], 0) === false) {
                printf("[%s][E] Truncate log file %s failed.\n", date('H:i:s', time()), $this->file_prefix.'-'.$log_level.'.log');
            }
        }
    }


    /**
     * 检查当前是否自动输出到控制台或标准输出
     *
     * @return unknown
     */
    public function isAutoOutput() {
        return (boolean)$this->auto_output;
    }


    /**
     * 设置自动输出到控制台或标准输出
     *
     * @param {boolean} $auto_output 是否自动输出
     * @return {boolean} 调用该方法之前的$auto_output设置
     */
    public function setAutoOutput($auto_output) {
        if ($this->auto_output != $auto_output) {
            $this->auto_output = (boolean)$auto_output;

            return !$auto_output;
        }

        return $this->auto_output;
    }


    /**
     * 获取日志文件前缀
     *
     * @return {string}
     */
    public function getLogFilePrefix() {
        return (string)$this->file_prefix;
    }


    /**
     * 设置日志文件前缀
     *
     * @param {string} $file_prefix 文件前缀
     */
    public function setLogFilePrefix($file_prefix) {
        if ($this->file_prefix != $file_prefix) {
            $this->file_prefix = $file_prefix;
        }
    }


    /**
     * 记录日志，写入日志文件
     *
     * @param {string} $msg 日志内容
     * @param {enum.<>} $level 日志等级
     * @return {string} 日志信息
     */
    private function log($msg, $level) {
        if (!isset($this->fps[$level])) {
            $file_name = $this->log_path.'/'.(!empty($this->file_prefix) ? ($this->file_prefix.'-') : '').$level.'.log';
            if (($this->fps[$level] = fopen($file_name, 'ab')) === false) {
                printf("[%s][E] Open log file %s failed.\n", date('H:i:s', time()), $file_name);

                return ;
            }
        }

        $fp = $this->fps[$level];
        if (fwrite($fp, $msg, strlen($msg)) === false) {
            printf("[%s][E] Write log '%s' to file %s failed.\n", date('H:i:s', time()), $msg, $this->file_prefix.'-'.$level.'.log');
        }

        return $msg;
    }


    /**
     * 生成一条日志信息
     *
     * @param {string} $msg 消息主要内容
     * @return {string} 格式化后的消息内容
     */
    public function info($msg) {
        $argv = func_get_args();
        if (count($argv) < 1) {
            printf("[%s][E] No enough arguments to log, at least one message argument to this function.\n", date('H:i:s', time()));

            return ;
        }

        /* some arguments there may be not a string, so, prepend @ to join to prevent warnings. */
        if (($msg = @call_user_func_array('sprintf', $argv)) === false) {
            printf("[%s][E] Format log message '%s' with '%s' failed.\n",
                   date('H:i:s', time()), array_shift($argv), @join(', ', $argv));

            return ;
        }

        $msg = sprintf("[%s][I] %s\n", date('H:i:s', time()), $msg);
        $this->log($msg, 'info');
        if ($this->auto_output) {
            print $msg;
        }

        return $msg;
    }


    /**
     * 生成一条错误信息
     *
     * @param {string} $msg 消息主要内容
     * @return {string} 格式化后的消息内容
     */
    public function error($msg) {
        $argv = func_get_args();
        if (count($argv) < 1) {
            printf("[%s][E] No enough arguments to log, at least one message argument to this function.\n", date('H:i:s', time()));

            return ;
        }

        /* some arguments there may be not a string, so, prepend @ to join to prevent warnings. */
        if (($msg = @call_user_func_array('sprintf', $argv)) === false) {
            printf("[%s][E] Format log message '%s' with '%s' failed.\n",
                   date('H:i:s', time()), array_shift($argv), @join(', ', $argv));

            return ;
        }

        $msg = sprintf("[%s][E] %s\n", date('H:i:s', time()), $msg);
        $this->log($msg, 'error');
        if ($this->auto_output) {
            print $msg;
        }

        return $msg;
    }


    /**
     * 生成一条警告信息
     *
     * @param {string} $msg 消息主要内容
     * @return {string} 格式化后的消息内容
     */
    public function warning($msg) {
        $argv = func_get_args();
        if (count($argv) < 1) {
            printf("[%s][E] No enough arguments to log, at least one message argument to this function.\n", date('H:i:s', time()));

            return ;
        }

        /* some arguments there may be not a string, so, prepend @ to join to prevent warnings. */
        if (($msg = @call_user_func_array('sprintf', $argv)) === false) {
            printf("[%s][E] Format log message '%s' with '%s' failed.\n",
                   date('H:i:s', time()), array_shift($argv), @join(', ', $argv));

            return ;
        }

        $msg = sprintf("[%s][W] %s\n", date('H:i:s', time()), $msg);
        $this->log($msg, 'warning');
        if ($this->auto_output) {
            print $msg;
        }

        return $msg;
    }


    /**
     * 打印一条跟踪信息
     *
     * @param {string} $msg 消息主要内容
     * @return {string} 格式化后的消息内容
     */
    public function trace($msg) {
        $argv = func_get_args();
        if (count($argv) < 1) {
            printf("[%s][E] No enough arguments to log, at least one message argument to this function.\n", date('H:i:s', time()));

            return ;
        }

        /* some arguments there may be not a string, so, prepend @ to join to prevent warnings. */
        if (($msg = @call_user_func_array('sprintf', $argv)) === false) {
            printf("[%s][E] Format log message '%s' with '%s' failed.\n",
                   date('H:i:s', time()), array_shift($argv), @join(', ', $argv));

            return ;
        }

        $msg = sprintf("[%s][T] %s\n", date('H:i:s', time()), $msg);
        $this->log($msg, 'trace');

        return $msg;
    }
}

